#include "esp8266.h"

static struct at_socket socketTab[SOCKET_MAX_NUM];
static int send_status = 0;
static char localIp[16];
static char localMac[32];

static int at_wait_connect(void)
{
    int retry_num = 50;
    struct at_response resp;

    resp.timeout = 1000;
    resp.line_num = 0;

    while (retry_num--)
    {
        at_obj_exec_cmd(&resp,"AT\r\n");
      
        if(wait_at_response() == 0)break;
    }
    
    if(retry_num == 0)
    {
        return -1;
    }

    return 0;
}

static void urc_busy_p_func(const char *data, int size)
{
    //printf("system is processing a commands...\r\n");
}

static void urc_busy_s_func(const char *data, int size)
{
    //printf("system is sending data...\r\n");
}

static void urc_send_func(const char *data, int size)
{
    if (strstr(data, "SEND OK"))
    {
        send_status = 1;//标记发送成功
    }
    else if (strstr(data, "SEND FAIL"))
    {

    }
}

static void urc_connect_func(const char *data, int size)
{
    int index = 0;

    sscanf(data, "%d,CONNECT", &index);

    socketTab[index].state = AT_SOCKET_CONNECT;
}


static void urc_close_func(const char *data, int size)
{
    int index = 0;

    sscanf(data, "%d,CLOSED", &index);

    memset(&socketTab[index],0,sizeof(struct at_socket));
    socketTab[index].state = AT_SOCKET_CLOSED;
    
    //printf("socket(%d) close\r\n",index);
}

static void urc_recv_func(const char *data, int size)
{
    int socket = 0; 
    int bfsz;
    int offset;
    int timeout;
    char temp[8] = {0};
    int temp_size = 0;
    
    ASSERT(data && size);

    sscanf(data, "+IPD,%d,%d:", &socket, (int *) &bfsz);

    /* set receive timeout by receive buffer length, not less than 10ms */
    timeout = bfsz > 10 ? bfsz : 10;

    if (socket < 0 || bfsz == 0)return;

    offset = socketTab[socket].bufLen;
    if(bfsz > (sizeof(socketTab[socket].buf) - offset))
    {
        printf("socket(%d) data buffer no memory receive(%d) \r\n",socket,bfsz);
         /* read and clean the coming data */
        while (temp_size < bfsz)
        {
            if (bfsz - temp_size > sizeof(temp))
            {
                at_client_obj_recv(temp, sizeof(temp), timeout);
            }
            else
            {
                at_client_obj_recv(temp, bfsz - temp_size, timeout);
            }
            temp_size += sizeof(temp);
        }
        return;
    }
    
    socketTab[socket].bufLen += bfsz;
    
    if (at_client_obj_recv(&socketTab[socket].buf[offset], bfsz, timeout) != bfsz)
    {
        printf("receive size(%d) data failed.\r\n",bfsz);
        return;
    }
    printf("\r\n");
}

static const struct at_urc urc_table[] =
{
    {"busy p",           "\r\n",           urc_busy_p_func},
    {"busy s",           "\r\n",           urc_busy_s_func},
    {"SEND OK",          "\r\n",           urc_send_func},
    {"SEND FAIL",        "\r\n",           urc_send_func},
    {"",                 ",CONNECT\r\n",   urc_connect_func},
    {"",                 ",CLOSED\r\n",    urc_close_func},
    {"+IPD",             ":",              urc_recv_func},
};

void esp8266_init(int port)
{
    int retry_num = 6;
    struct at_response resp;
    int socket = 0;
    const char *resp_expr = "%*[^\"]\"%[^\"]\"";
    
    at_client_init();
    
    at_obj_set_urc_table(urc_table, sizeof(urc_table) / sizeof(urc_table[0]));
    
    resp.line_num = 0;
    resp.timeout = 5000;
    
    memset(socketTab,0,sizeof(socketTab));
    
    if (at_wait_connect() < 0)
    {
        printf("connect esp8266 failed\r\n");
        return;
    }
    
    while (retry_num--)
    {
        at_obj_exec_cmd(&resp,"AT+RST\r\n");
        if(wait_at_response() < 0)continue;
        delay_ms(1000);
        
        at_obj_exec_cmd(&resp,"ATE0\r\n");
        if(wait_at_response() < 0)continue;
        
        at_obj_exec_cmd(&resp,"AT+CWMODE=2\r\n");
        if(wait_at_response() < 0)continue;
        
        at_obj_exec_cmd(&resp,"AT+CWSAP=\"%s\",\"%s\",6,4\r\n",ESP8266_WIFI_SSID,ESP8266_WIFI_PASSWORD);
        if(wait_at_response() < 0)continue;
        
        at_obj_exec_cmd(&resp,"AT+CIPMUX=1\r\n");
        if(wait_at_response() < 0)continue;
        
        at_obj_exec_cmd(&resp,"AT+CIPSERVER=1,%d\r\n",port);
        if(wait_at_response() < 0)continue;
        
        at_obj_exec_cmd(&resp,"AT+CIFSR\r\n");
        if(wait_at_response() < 0)continue;
        
        if (at_resp_parse_line_args_by_kw(&resp, "APIP", resp_expr, localIp) <= 0)
        {
            continue;
        }
        
        if (at_resp_parse_line_args_by_kw(&resp, "APMAC", resp_expr, localMac) <= 0)
        {
            continue;
        }
        break;
    }
    
    if(retry_num == 0)
    {
        printf("esp8266 init failed\r\n");
    }
    
    for(socket=0; socket<SOCKET_MAX_NUM; socket++)
    {
        socketTab[socket].state = AT_SOCKET_LISTEN;
    }
    
    printf("local ip :%s local mac:%s\r\n",localIp,localMac);
}

int at_listen(void)
{
    static int socket = 0;
    struct at_response resp;

    resp.line_num = 0;
    resp.timeout = 1000;
    
    socket = socket % SOCKET_MAX_NUM;
    
    for(; socket<SOCKET_MAX_NUM; socket++)
    {
        if(socketTab[socket].state == AT_SOCKET_CONNECT)
        {
            #if 1
            if(strlen(socketTab[socket].clientIp)==0)
            {
                at_obj_exec_cmd(&resp, "AT+CWLIF\r\n", socket);
                if(wait_at_response() == 0)
                {
                    at_resp_parse_line_args(&resp, 1, "%[^,],%[^,\r]", 
                    socketTab[socket].clientIp,socketTab[socket].clientMac);
                    
                    printf("remote ip :%s remote mac:%s connected\r\n",
                    socketTab[socket].clientIp,socketTab[socket].clientMac);
                }
            }
            #endif
            socket++;
            return socket-1;
        }
        else if(socketTab[socket].state == AT_SOCKET_CLOSED)
        {
            socket++;
            return socket-1;
        }
    }
    return -1;
}

int at_recv(int socket,void *mem, size_t len)
{
    if(socketTab[socket].state != AT_SOCKET_CONNECT) return -1;
    
    if(socketTab[socket].bufLen > 0)
    {
        if(len > socketTab[socket].bufLen)
        {
            len = socketTab[socket].bufLen;
        }
        memcpy(mem,socketTab[socket].buf,len);
        socketTab[socket].bufLen -= len;
        memmove(socketTab[socket].buf, &socketTab[socket].buf[len],socketTab[socket].bufLen);
        return len;
    }
    return 0;
}

int at_send(int socket, const char *buff, size_t bfsz)
{
    int sent_size = 0;
    int cur_pkt_size = 0;
    uint32_t prev_tick = 0;
    struct at_response resp;

    if(socketTab[socket].state != AT_SOCKET_CONNECT) return -1;
        
    resp.line_num = 2;
    resp.timeout = 1000;
    
    at_obj_set_end_sign('>');

    while (sent_size < bfsz)
    {
        if (bfsz - sent_size < ESP8266_MODULE_SEND_MAX_SIZE)
        {
            cur_pkt_size = bfsz - sent_size;
        }
        else
        {
            cur_pkt_size = ESP8266_MODULE_SEND_MAX_SIZE;
        }

        at_obj_exec_cmd(&resp, "AT+CIPSEND=%d,%d\r\n", socket, cur_pkt_size);

        if(wait_at_response() < 0)
        {
            at_obj_set_end_sign(0);
            printf("AT+CIPSEND failed \r\n");
            return -1;
        }
  
        send_status = 0;
        at_client_obj_send(buff + sent_size, cur_pkt_size);

        prev_tick = at_tick_get();
        while (1)
        {
            /* Check whether it is timeout */
            if (at_tick_elaps(prev_tick) > 5000)
            {
                at_obj_set_end_sign(0);
                return -1;
            }
            
            at_parser();
            
            if(send_status == 1)break;
        }
 
        sent_size += cur_pkt_size;
    }

    at_obj_set_end_sign(0);

    return sent_size;
}

int at_close(int socket)
{
    struct at_response resp;

    resp.line_num = 0;
    resp.timeout = 5000;
    
    if(socketTab[socket].state == AT_SOCKET_CLOSED)
    {
        socketTab[socket].state = AT_SOCKET_LISTEN;
        return 0;
    }
    
    if(socketTab[socket].state == AT_SOCKET_LISTEN)
    {
        return 0;
    }
    
    at_obj_exec_cmd(&resp, "AT+CIPCLOSE=%d\r\n", socket);

    if(wait_at_response() == 0)
    {
        memset(&socketTab[socket],0,sizeof(struct at_socket));
        socketTab[socket].state = AT_SOCKET_LISTEN;
        return 0;
    }
    return -1;
}
